#include <REGX51.H>
#include "keypad.h"
#include "millis.h"

bool single_key = FALSE;
bool stateChanged = TRUE;
unsigned char kchar = NO_KEY;
unsigned long startTime = 0;
const unsigned char code debounceTime = 10;
const unsigned char code holdTime = 500;
unsigned long holdTimer;
void (*keypadEventListener)(char);

unsigned char bitMap[3];   //3 row x 8 column array of bits.
Key key[LIST_MAX]=
{
	{NO_KEY, -1, IDLE, FALSE},
	{NO_KEY, -1, IDLE, FALSE},
	{NO_KEY, -1, IDLE, FALSE}
};

unsigned char code keymap[3][4]=
{
	{'1', '2', '3', 'C'},
	{'4', '5', '6', '0'},
	{'7', '8', '9', 'E'}
};

unsigned char getKey()
{
	single_key = TRUE;

	//if (getKeys() && key[0].stateChanged && (key[0].kstate==PRESSED))
	//	return key[0].kchar;

	if (key[0].kstate==PRESSED)
	return 'Y';
	
	single_key = FALSE;

	return NO_KEY;
}

bool getKeys() 
{
	bool keyActivity = FALSE;

	if ( (millis()-startTime)>debounceTime ) 
	{
		scanKeys();
		keyActivity = updateList();
		startTime = millis();
	}

	return keyActivity;
}


void scanKeys() 
{
	unsigned char readData;
	
	P3 = 0xFE;   //ROW0 = 0;
	readData = P3; //Read column
	bitWrite(&bitMap[0], 0, !(readData&0x10));
	bitWrite(&bitMap[0], 1, !(readData&0x20));
	bitWrite(&bitMap[0], 2, !(readData&0x40));
	bitWrite(&bitMap[0], 3, !(readData&0x80));
	
	P3 = 0xFD;   //ROW1 = 0;
	readData = P3; //Read column
	bitWrite(&bitMap[1], 0, !(readData&0x10));
	bitWrite(&bitMap[1], 1, !(readData&0x20));
	bitWrite(&bitMap[1], 2, !(readData&0x40));
	bitWrite(&bitMap[1], 3, !(readData&0x80));
	
	P3 = 0xFB;   //ROW2 = 0;
	readData = P3; //Read column
	bitWrite(&bitMap[2], 0, !(readData&0x10));
	bitWrite(&bitMap[2], 1, !(readData&0x20));
	bitWrite(&bitMap[2], 2, !(readData&0x40));
	bitWrite(&bitMap[2], 3, !(readData&0x80));
}

bool updateList() 
{
	unsigned char i, r, c;
	bool anyActivity = FALSE;

	for (i=0; i<LIST_MAX; i++) {
		if (key[i].kstate==IDLE) {
			key[i].kchar = NO_KEY;
			key[i].kcode = -1;
			key[i].stateChanged = FALSE;
		}
	}

	for (r=0; r<ROWS; r++) 
	{
		for (c=0; c<COLUMNS; c++) 
		{
			bool button = bitRead(bitMap[r],c);
			unsigned char keyChar = keymap[r * COLUMNS + c];
			unsigned char keyCode = r * COLUMNS + c;
			unsigned char idx = findInList (keyCode);

			if (idx > -1)	
			{
				nextKeyState(idx, button);
			}

			if ((idx == -1) && button) 
			{
				for (i=0; i<LIST_MAX; i++) 
				{
					if (key[i].kchar==NO_KEY) 
					{		
						key[i].kchar = keyChar;
						key[i].kcode = keyCode;
						key[i].kstate = IDLE;		
						nextKeyState (i, button);
						break;	
					}
				}
			}
		}
	}


	for (i=0; i<LIST_MAX; i++) {
		if (key[i].stateChanged) anyActivity = TRUE;
	}

	return anyActivity;
}


void nextKeyState(unsigned char idx, bool button) 
{
	key[idx].stateChanged = FALSE;

	switch (key[idx].kstate) {
		case IDLE:
			if (button==CLOSED) {
				transitionTo (idx, PRESSED);
				holdTimer = millis(); }	
			break;
		case PRESSED:
			if ((millis()-holdTimer)>holdTime)
				transitionTo (idx, HOLD);
			else if (button==OPEN)
				transitionTo (idx, RELEASED);
			break;
		case HOLD:
			if (button==OPEN)
				transitionTo (idx, RELEASED);
			break;
		case RELEASED:
			transitionTo (idx, IDLE);
			break;
	}
}

void bitWrite(unsigned char *x, unsigned char n, bool b)
{
	if(b)
	{
		*x = *x | (1 << n);
	}
	else
	{
		*x = *x & (~(1 << n));
	}
}


bool bitRead(unsigned char x, unsigned char n)
{
	return x & (1 << n);
}

unsigned char findInList (unsigned char keyChar)
{
	unsigned char i;
	for (i=0; i<LIST_MAX; i++) {
		if (key[i].kchar == keyChar) {
			return i;
		}
	}
	return -1;
}


void transitionTo(unsigned char idx, KeyState nextState) 
{
	key[idx].kstate = nextState;
	key[idx].stateChanged = TRUE;

	// Sketch used the getKey() function.
	// Calls keypadEventListener only when the first key in slot 0 changes state.
	if (single_key)  {
	  	if ( (keypadEventListener!=NULL) && (idx==0) )  {
			keypadEventListener(key[0].kchar);
		}
	}
	// Sketch used the getKeys() function.
	// Calls keypadEventListener on any key that changes state.
	else {
	  	if (keypadEventListener!=NULL)  {
			keypadEventListener(key[idx].kchar);
		}
	}
}